package site.wenneng.dev.conf;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import javafx.util.Pair;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import org.openqa.selenium.By;
import org.openqa.selenium.Cookie;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import site.wenneng.dev.common.enumeration.RemoteSiteEnum;

import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
public class HeaderInterceptor implements Interceptor {

    private Map<RemoteSiteEnum, Headers> headersMap = new HashMap<>();

    public HeaderInterceptor() {
        loadMap();
    }

    private static List<SiteHeaderProperties> loadSiteProperties(File file) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        TypeReference<List<SiteHeaderProperties>> typeReference = new TypeReference<List<SiteHeaderProperties>>() {
        };
        return objectMapper.readValue(file, typeReference);
    }

    private static void executeLogin(WebDriver driver) {
        driver.get("https://dev.hundsun.com/frameV2/cicd/personal-voucher");
        new WebDriverWait(driver, Duration.ofSeconds(10))
                .until(ExpectedConditions.presenceOfElementLocated(By.id("username")));
        try {
            while (driver.getCurrentUrl().startsWith("https://hs-cas.hundsun.com/")) {
                driver.findElement(By.id("username")).sendKeys("hewn23952");
                driver.findElement(By.id("password")).sendKeys("wending@2");
                TimeUnit.MILLISECONDS.sleep(200);
                driver.findElement(By.xpath("//div[@id='submit_psd']/input")).click();
                TimeUnit.MILLISECONDS.sleep(500);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @NotNull
    private static WebDriver getWebDriver() {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless");
        options.addArguments("--remote-allow-origins=*");
        return new ChromeDriver(options);
    }

    @NotNull
    private static String getCookies(WebDriver driver) {
        return driver.manage().getCookies().stream()
                .map(item -> item.getName() + "=" + item.getValue())
                .collect(Collectors.joining("; "));
    }

    @SuppressWarnings("SameParameterValue")
    private static String getCookie(WebDriver driver, String name) {
        Cookie cookieNamed = driver.manage().getCookieNamed(name);
        return Optional.ofNullable(cookieNamed).map(Cookie::getValue).orElse("");
    }

    @SneakyThrows
    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) {
        RemoteSiteEnum remoteSite = RemoteSiteEnum.fitUrl(chain.request().url().toString());
        if (remoteSite == null) {
            return chain.proceed(chain.request());
        }
        Headers headers = headersMap.get(remoteSite);
        if (headers != null) {
            Response res = chain.proceed(new Request.Builder(chain.request()).headers(headers).build());
            boolean isHtmlContentType = Optional.ofNullable(res.header("Content-Type")).orElse("").startsWith("text/html");
            if (res.code() != 401 && !isHtmlContentType) {
                return res;
            }
            res.close();
        }
        refreshHeadersByChrome();
        headers = headersMap.get(remoteSite);
        return chain.proceed(new Request.Builder(chain.request()).headers(headers).build());
    }

    @SneakyThrows
    public void loadMap() {
        File file = new File("site.conf");
        if (!file.exists()) {
            refreshHeadersByChrome();
        }
        this.headersMap = sitePropertiesToMap(loadSiteProperties(file));
        log.info("从配置文件site.conf中读取站点配置信息完成");
    }

    public void refreshHeadersByChrome() {
        Map<RemoteSiteEnum, Headers> localMap = this.headersMap;
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        try {
            if (localMap != this.headersMap) {
                return;
            }
            WebDriver driver = getWebDriver();
            executeLogin(driver);
            List<SiteHeaderProperties> properties = Arrays.asList(
                    initFineBiSite(driver),
                    initDevSite(driver)
            );
            driver.quit();
            this.headersMap = sitePropertiesToMap(properties);
            saveSiteProperties(new File("site.conf"), properties);
            log.info("调用浏览器抓取站点配置信息完成");
        } finally {
            lock.unlock();
        }
    }

    private void saveSiteProperties(File file, List<SiteHeaderProperties> properties) {
        try {
            if (file.exists() || file.createNewFile()) {
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.writeValue(file, properties);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<RemoteSiteEnum, Headers> sitePropertiesToMap(List<SiteHeaderProperties> properties) {
        return properties.stream().map(item -> {
            Headers.Builder builder = new Headers.Builder();
            item.getProperties().forEach(each -> builder.add(each.getName(), each.getValue()));
            return new Pair<>(RemoteSiteEnum.valueOf(item.getSite()), builder.build());
        }).collect(Collectors.toMap(Pair::getKey, Pair::getValue, (o1, o2) -> o1));
    }

    @SneakyThrows
    public SiteHeaderProperties initDevSite(WebDriver driver) {
        driver.get("https://dev.hundsun.com/frameV2/cicd/personal-voucher");
        new WebDriverWait(driver, Duration.ofSeconds(10))
                .until(ExpectedConditions.presenceOfElementLocated(By.id("root")));
        List<SiteHeaderProperty> list = Arrays.asList(
                new SiteHeaderProperty("Authorization", getLocalStorageItem(driver, "TOKEN")),
                new SiteHeaderProperty("Cookie", getCookies(driver)),
                new SiteHeaderProperty("Host", "dev.hundsun.com"),
                new SiteHeaderProperty("Referer", "https://dev.hundsun.com/frameV2/pms/storyNew"),
                new SiteHeaderProperty("Origin", RemoteSiteEnum.DEV.getOrigin()),
                new SiteHeaderProperty("rights-group", "45"),
                new SiteHeaderProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")
        );
        return new SiteHeaderProperties(RemoteSiteEnum.DEV.name(), list);
    }

    private SiteHeaderProperties initFineBiSite(WebDriver driver) {
        driver.get("https://finebi.hundsun.com/webroot/decision/v5/design/report/f6f11f33dee74fb9bdfc9dcca2e6313e/view?entryType=5");
        new WebDriverWait(driver, Duration.ofSeconds(10))
                .until(ExpectedConditions.presenceOfElementLocated(By.id("body")));
        List<SiteHeaderProperty> list = Arrays.asList(
                new SiteHeaderProperty("Authorization", "Bearer" + new String(Base64.getEncoder().encode("\n".getBytes())) + getCookie(driver, "fine_auth_token")),
                new SiteHeaderProperty("Cookie", getCookies(driver)),
                new SiteHeaderProperty("Host", "finebi.hundsun.com"),
                new SiteHeaderProperty("Referer", "https://finebi.hundsun.com/webroot/decision/v5/design/report/f6f11f33dee74fb9bdfc9dcca2e6313e/view?entryType=5"),
                new SiteHeaderProperty("Origin", RemoteSiteEnum.FINE_BI.getOrigin()),
                new SiteHeaderProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36")
        );
        return new SiteHeaderProperties(RemoteSiteEnum.FINE_BI.name(), list);
    }

    @SuppressWarnings("SameParameterValue")
    private String getLocalStorageItem(WebDriver driver, String key) {
        JavascriptExecutor javascriptExecutor = ((JavascriptExecutor) driver);
        return (String) javascriptExecutor.executeScript("return window.localStorage.getItem(arguments[0]);", key);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SiteHeaderProperties {
        String site;
        List<SiteHeaderProperty> properties;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class SiteHeaderProperty {

        private String name;
        private String value;
    }
}
